#ifdef _WIN32
#include <windows.h>
#endif

#include <string>
#include <iostream>
#include "glee/GLee.h"
#include <GL/glu.h>

#include "skybox.h"
#include "targa.h"
#include "glslshader.h"
#include "spherecollider.h"

using std::string;

GLuint Skybox::m_skyboxTexID = 0;
GLuint Skybox::m_vertexBuffer = 0;
GLuint Skybox::m_texCoordBuffer = 0;
GLuint Skybox::m_indexBuffer = 0;
GLuint Skybox::m_colorBuffer = 0;
std::auto_ptr<GLSLProgram> Skybox::m_shaderProgram;

const string SKYBOX_TEXTURE = "data/textures/skybox.tga";

const string VERTEX_SHADER_120 = "data/shaders/glsl1.20/basic-fixed.vert";
const string VERTEX_SHADER_130 = "data/shaders/glsl1.30/basic-fixed.vert";

const string FRAGMENT_SHADER_120 = "data/shaders/glsl1.20/basic-fixed.frag";
const string FRAGMENT_SHADER_130 = "data/shaders/glsl1.30/basic-fixed.frag";

Skybox::Skybox(GameWorld* const world):
Entity(world)
{
    m_collider = NULL;
	setGravity(false);
	setVelocity(0.0f,0.0f,0.0f);
}

Skybox::~Skybox()
{
    delete m_collider;
}

void Skybox::onPrepare(float dT)
{

}

void Skybox::onRender() const
{
    static float modelviewMatrix[16];
    static float projectionMatrix[16];

    glPushMatrix();
	glTranslatef(m_position.x, m_position.y, m_position.z);

    glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
    glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);

    m_shaderProgram->bindShader();
    m_shaderProgram->sendUniform4x4("modelview_matrix", modelviewMatrix);
    m_shaderProgram->sendUniform4x4("projection_matrix", projectionMatrix);
	m_shaderProgram->sendUniform("texture0", 0);

    glBindTexture(GL_TEXTURE_2D, m_skyboxTexID);

	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glEnableVertexAttribArray(0); //Enable the vertex attribute
    glEnableVertexAttribArray(1); //Enable the color attribute
    glEnableVertexAttribArray(2); //Enable the texture coordinate attribute

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexAttribPointer((GLint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);	

	// SPECIFY VERTEX COLORS TO USE
	glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
    glVertexAttribPointer((GLint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);

	// SPECIFY TEXTURING TO USE
	glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
    glVertexAttribPointer((GLint)2, 2, GL_FLOAT, GL_FALSE, 0, 0);

	// SPECIFY INDICES TO USE
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);

	// AND RENDER
 	glDrawElements(GL_TRIANGLES, indexBuffer.size(), GL_UNSIGNED_INT, 0);

	glDisableVertexAttribArray(2);
	glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    /*glDisable(GL_CULL_FACE);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexAttribPointer((GLint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
    glVertexAttribPointer((GLint)1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);

    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);
    glEnable(GL_CULL_FACE);*/

    glPopMatrix();
}

void Skybox::onPostRender()
{

}

void Skybox::initializeVBOs()
{
	glGenBuffers(1, &m_vertexBuffer);
	glGenBuffers(1, &m_texCoordBuffer);
	glGenBuffers(1, &m_indexBuffer);
	glGenBuffers(1, &m_colorBuffer);

	generateVertices();
	generateIndices();
	generateVertexColors(); 
	generateTextureCoords();

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
    glColorPointer(3, GL_FLOAT, 0, 0); 

    /*GLfloat vertex [] = {
        -1.0f, -1.0f, 0.0f, //First Square vertex
         1.0f, -1.0f, 0.0f,
        -1.0f, 1.0f, 0.0f,
         1.0f, 1.0f, 0.0f,  //Last Square vertex
         0.0f, -1.0f, 1.0f, //First Square vertex
         0.0f, -1.0f,-1.0f,
         0.0f, 1.0f, 1.0f,  //Last Square vertex
         0.0f, 1.0f,-1.0f,

    };

    GLfloat texCoord [] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };

    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8 * 3, &vertex[0], GL_STATIC_DRAW); //Send the data to OpenGL

    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8 * 2, &texCoord[0], GL_STATIC_DRAW); //Send the data to OpenGL*/
}

void Skybox::generateVertices()
{
    vertexBuffer.push_back(Vertex(-3.0f,  3.0f, -3.0f)); // 0
	vertexBuffer.push_back(Vertex( 3.0f,  3.0f, -3.0f)); // 1
	vertexBuffer.push_back(Vertex( 3.0f, -3.0f, -3.0f)); // 2
    vertexBuffer.push_back(Vertex(-3.0f, -3.0f, -3.0f)); // 3
    vertexBuffer.push_back(Vertex(-3.0f,  3.0f,  3.0f)); // 4
	vertexBuffer.push_back(Vertex( 3.0f,  3.0f,  3.0f)); // 5
	vertexBuffer.push_back(Vertex( 3.0f, -3.0f,  3.0f)); // 6
    vertexBuffer.push_back(Vertex(-3.0f, -3.0f,  3.0f)); // 7

	//REPEATS
	vertexBuffer.push_back(Vertex(-3.0f,  3.0f,  3.0f)); // 8 -- 4b
	vertexBuffer.push_back(Vertex( 3.0f,  3.0f,  3.0f)); // 9 -- 5b
	vertexBuffer.push_back(Vertex( 3.0f,  3.0f,  3.0f)); // 10 -- 5c
	vertexBuffer.push_back(Vertex( 3.0f, -3.0f,  3.0f)); // 11 -- 6b
	vertexBuffer.push_back(Vertex( 3.0f, -3.0f,  3.0f)); // 12 -- 6c
	vertexBuffer.push_back(Vertex(-3.0f, -3.0f,  3.0f)); // 13 -- 7b

	// NOW SEND THE VERTEX DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * vertexBuffer.size(), &vertexBuffer[0], GL_STATIC_DRAW);
}
void Skybox::generateIndices()
{
    // A 5-SIDED PYRAMID NEEDS 6 TRIANGLES
	// WE'LL USE RIGHT-HANDED COORDINATES, WHICH IS THE DEFAULT IN OpenGL

	// BACK
	addTriangle(Triangle(0,2,1));
	addTriangle(Triangle(0,3,2));

	// TOP
	addTriangle(Triangle(0,5,1));
	addTriangle(Triangle(0,4,5));
	
	//BOTTOM
	addTriangle(Triangle(3,6,2));
	addTriangle(Triangle(3,7,6));

	// RIGHT
	addTriangle(Triangle(9,2,1));
	addTriangle(Triangle(9,11,2));

	//LEFT
	addTriangle(Triangle(0,13,8));
	addTriangle(Triangle(0,3,13));

	//FRONT
	addTriangle(Triangle(8,12,10));
	addTriangle(Triangle(8,13,12));
	
	// NOW SEND THE INDEX DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexBuffer.size(), &indexBuffer[0], GL_STATIC_DRAW);
}

void Skybox::generateTextureCoords()
{
	
	/*
	m_texCoords.push_back(TexCoord(0.5f,  0.5f));	// VERTEX 0
	m_texCoords.push_back(TexCoord(0.75f, 0.5f));	// VERTEX 1
	m_texCoords.push_back(TexCoord(0.75f, 0.25f));	// VERTEX 2
	m_texCoords.push_back(TexCoord(0.5f,  0.25f));	// VERTEX 3
	m_texCoords.push_back(TexCoord(0.5f,  0.75f));	// VERTEX 4
	m_texCoords.push_back(TexCoord(0.75f, 0.75f));	// VERTEX 5
	m_texCoords.push_back(TexCoord(0.75f, 0.0f));	// VERTEX 6
	m_texCoords.push_back(TexCoord(0.5f,  0.0f));	// VERTEX 7
	m_texCoords.push_back(TexCoord(0.25f, 0.5f));	// VERTEX 8
	m_texCoords.push_back(TexCoord(1.0f,  0.5f));	// VERTEX 9
	m_texCoords.push_back(TexCoord(0.0f,  0.5f));	// VERTEX 10
	m_texCoords.push_back(TexCoord(1.0f,  0.25f));	// VERTEX 11
	m_texCoords.push_back(TexCoord(0.0f,  0.25f));	// VERTEX 12
	m_texCoords.push_back(TexCoord(0.25f, 0.25f));	// VERTEX 13
	*/
	m_texCoords.push_back(TexCoord(0.500488281f,	0.499023438f));	// VERTEX 0
	m_texCoords.push_back(TexCoord(0.749023438f,	0.499023438f));	// VERTEX 1
	m_texCoords.push_back(TexCoord(0.749023438f,	0.25f));	// VERTEX 2
	m_texCoords.push_back(TexCoord(0.500488281f,	0.25f));	// VERTEX 3
	m_texCoords.push_back(TexCoord(0.500488281f,	0.749511719f));	// VERTEX 4
	m_texCoords.push_back(TexCoord(0.749023438f,	0.749511719f));	// VERTEX 5
	m_texCoords.push_back(TexCoord(0.749023438f,	0.00048828125f));	// VERTEX 6
	m_texCoords.push_back(TexCoord(0.500488281f,	0.00048828125f));	// VERTEX 7
	m_texCoords.push_back(TexCoord(0.25f,			0.499023438f));	// VERTEX 8
	m_texCoords.push_back(TexCoord(0.999511719f,	0.499023438f));	// VERTEX 9
	m_texCoords.push_back(TexCoord(0.00048828125f,  0.499023438f));	// VERTEX 10
	m_texCoords.push_back(TexCoord(0.999511719f,	0.25f));	// VERTEX 11
	m_texCoords.push_back(TexCoord(0.00048828125f,  0.25f));	// VERTEX 12
	m_texCoords.push_back(TexCoord(0.25f,			0.25f));	// VERTEX 13
    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_texCoords.size() * 2, &m_texCoords[0], GL_STATIC_DRAW); //Send the data to OpenGL
}

void Skybox::generateVertexColors()
{
	for (int i = 0; i < vertexBuffer.size(); i++)
		colorBuffer.push_back(Color(1.0f, 1.0f, 1.0f, 1.0f));
 

    // SEND THE COLOR DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * colorBuffer.size(), &colorBuffer[0], GL_STATIC_DRAW);
}

void Skybox::addTriangle(Triangle& triangleToAdd)
{
	indexBuffer.push_back(triangleToAdd.v1);
	indexBuffer.push_back(triangleToAdd.v2);
	indexBuffer.push_back(triangleToAdd.v3);
}
bool Skybox::onInitialize()
{
    if(m_skyboxTexID == 0)
    {
        TargaImage skyboxTexture;
        if (!skyboxTexture.load(SKYBOX_TEXTURE))
        {
            std::cerr << "Couldn't load the skybox texture" << std::endl;
            return false;
        }

        initializeVBOs();

        const string vertexShader = (GLSLProgram::glsl130Supported()) ? VERTEX_SHADER_130 : VERTEX_SHADER_120;
        const string fragmentShader = (GLSLProgram::glsl130Supported()) ? FRAGMENT_SHADER_130 : FRAGMENT_SHADER_120;

        m_shaderProgram = std::auto_ptr<GLSLProgram>(new GLSLProgram(vertexShader, fragmentShader));

        if (!m_shaderProgram->initialize())
        {
            std::cerr << "Could not initialize the skybox shaders" << std::endl;
            return false;
        }

        m_shaderProgram->bindAttrib(0, "a_Vertex");
		m_shaderProgram->bindAttrib(1, "a_Color");
		m_shaderProgram->bindAttrib(2, "a_TexCoord");
        m_shaderProgram->linkProgram();

        glGenTextures(1, &m_skyboxTexID);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_skyboxTexID);
        /*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);*/

		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        //We load the texture with an alpha channel (RGBA)
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, skyboxTexture.getWidth(),
                      skyboxTexture.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                      skyboxTexture.getImageData());
    }

    return true;

}

void Skybox::onShutdown()
{

}
